//
// Copyright (c) 2010-2024 Antmicro
//
// This file is licensed under the MIT License.
// Full license text is available in 'licenses/MIT.txt'.
//
using System;
using System.Collections.Generic;
using System.Linq;
using Antmicro.Renode.Utilities;
using Antmicro.Renode.Utilities.Packets;

namespace Antmicro.Renode.Peripherals.SENT
{
    public class FastMessage
    {
        public static byte CalculateCRC(IEnumerable<byte> nibbles)
        {
            // CRCEngine is not used here as it assumes working on bytes instead of nibbles
            byte value = 5;
            foreach(var nibble in nibbles)
            {
                value = CRCTable[(value << 4) + nibble];
            }
            return CRCTable[value << 4];
        }
        
        public FastMessage(params byte[] nibbles)
        {
            if(nibbles.Length < 1 || nibbles.Length > MaxFastMessageLength)
            {
                throw new ArgumentException($"{nameof(nibbles)} Length ({nibbles.Length}) is out of range of [1;{MaxFastMessageLength}]");
            }

            Nibbles = new byte[nibbles.Length + 1];
            Array.Copy(nibbles, Nibbles, nibbles.Length);
            Nibbles[Nibbles.Length - 1] = CalculateCRC(nibbles);
        }

        private static readonly byte[] CRCTable = new byte[256]
        {
            0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
            0xD, 0xC, 0xF, 0xE, 0x9, 0x8, 0xB, 0xA, 0x5, 0x4, 0x7, 0x6, 0x1, 0x0, 0x3, 0x2,
            0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0, 0xF, 0xE, 0xD, 0xC, 0xB, 0xA, 0x9, 0x8,
            0xA, 0xB, 0x8, 0x9, 0xE, 0xF, 0xC, 0xD, 0x2, 0x3, 0x0, 0x1, 0x6, 0x7, 0x4, 0x5,
            0xE, 0xF, 0xC, 0xD, 0xA, 0xB, 0x8, 0x9, 0x6, 0x7, 0x4, 0x5, 0x2, 0x3, 0x0, 0x1,
            0x3, 0x2, 0x1, 0x0, 0x7, 0x6, 0x5, 0x4, 0xB, 0xA, 0x9, 0x8, 0xF, 0xE, 0xD, 0xC,
            0x9, 0x8, 0xB, 0xA, 0xD, 0xC, 0xF, 0xE, 0x1, 0x0, 0x3, 0x2, 0x5, 0x4, 0x7, 0x6,
            0x4, 0x5, 0x6, 0x7, 0x0, 0x1, 0x2, 0x3, 0xC, 0xD, 0xE, 0xF, 0x8, 0x9, 0xA, 0xB,
            0x1, 0x0, 0x3, 0x2, 0x5, 0x4, 0x7, 0x6, 0x9, 0x8, 0xB, 0xA, 0xD, 0xC, 0xF, 0xE,
            0xC, 0xD, 0xE, 0xF, 0x8, 0x9, 0xA, 0xB, 0x4, 0x5, 0x6, 0x7, 0x0, 0x1, 0x2, 0x3,
            0x6, 0x7, 0x4, 0x5, 0x2, 0x3, 0x0, 0x1, 0xE, 0xF, 0xC, 0xD, 0xA, 0xB, 0x8, 0x9,
            0xB, 0xA, 0x9, 0x8, 0xF, 0xE, 0xD, 0xC, 0x3, 0x2, 0x1, 0x0, 0x7, 0x6, 0x5, 0x4,
            0xF, 0xE, 0xD, 0xC, 0xB, 0xA, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0,
            0x2, 0x3, 0x0, 0x1, 0x6, 0x7, 0x4, 0x5, 0xA, 0xB, 0x8, 0x9, 0xE, 0xF, 0xC, 0xD,
            0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
            0x5, 0x4, 0x7, 0x6, 0x1, 0x0, 0x3, 0x2, 0xD, 0xC, 0xF, 0xE, 0x9, 0x8, 0xB, 0xA,
        };

        public byte[] Nibbles { get; }

        private const int MaxFastMessageLength = 6;
    }

    public class SlowMessage : FastMessage
    {
        public SlowMessage(params byte[] nibbles) : base(nibbles)
        {
            if(nibbles.Length != ShortMessageNibbles)
            {
                throw new ArgumentException($"Invalid nibble count. {ShortMessageNibbles} expected, got {nibbles.Length}");
            }
        }

        public IEnumerable<bool> Bits
        {
            get
            {
                foreach(var nibble in Nibbles)
                {
                    for(byte i = 0; i < 4; i++)
                    {
                        // Data from the slow messages is transmitted MSB first
                        yield return BitHelper.IsBitSet(nibble, (byte)(3 - i));
                    }
                }
            }
        }

        private const int ShortMessageNibbles = 3;
    }
}
